﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;

namespace CommonEngine
{
    /// <summary>
    /// 反射助手
    /// </summary>
    public static class ReflectionFactory
    {
        public static T GreateByType<T>(object[] ParameterList = null) where T : class
        {
            var obj = GreateByType(typeof(T), ParameterList);
            return obj == null ? default(T) : (T)obj;
        }

        public static T GreateByTypeName<T>(string vt, object[] ParameterList = null) where T : class
        {
            var obj = GreateByString(vt, ParameterList);
            return obj == null ? default(T) : (T)obj;
        }

        /// <summary>
        /// 通过类型进行反射动态实例化
        /// </summary>
        public static object GreateByType(Type vt, object[] ParameterList = null)
        {
            if (vt == null)
                return null;

            object oo = null;
            try
            {
                if (ParameterList == null || ParameterList.Length <= 0)
                    oo = Activator.CreateInstance(vt);
                else
                    oo = Activator.CreateInstance(vt, ParameterList);
            }
            catch (Exception e)
            {
                throw e;
            }

            return oo;
        }

        /// <summary>
        /// 通过类地址及构造参数，动态生成实例
        /// eg:RefFactory.GreateByString(Hw.Crm.Model.TestEn,Hw.Crm.Model)  
        /// --Hw.Crm.Model为命名空间，同时也是dll的名称--TestEn是实体类名
        /// </summary>
        public static object GreateByString(string vt, object[] ParameterList = null)
        {
            if (string.IsNullOrEmpty(vt))
                return null;

            Type t = null;
            Assembly am = null;
            t = GetType(vt, out am);

            object obj = null;

            if (t == null)
            {
                return obj;
            }

            try
            {
                if (ParameterList == null || ParameterList.Length <= 0)
                {
                    obj = Activator.CreateInstance(t);
                }
                else
                {
                    obj = Activator.CreateInstance(t, ParameterList);
                }
            }
            catch
            {
                throw;
            }

            return obj;
        }
        public static Type GetType(string TypeName, out Assembly amOut)
        {
            if (string.IsNullOrEmpty(TypeName))
            {
                amOut = null;
                return null;
            }

            string ams = null;
            if (TypeName.IndexOf(',', 0) < 0)
            {
                if (TypeName.IndexOf('.', 0) < 0)
                {
                    amOut = null;
                    return null;
                }
                ams = TypeName.Substring(0, TypeName.IndexOf('.', 0));
            }
            else
            {
                string[] ary = TypeName.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                TypeName = ary[0];
                ams = ary[1];
            }

            Assembly am = null;
            Type tt = null;

            try
            {
                am = Assembly.Load(ams);
            }
            catch
            {
                throw;
            }

            if (am != null)
                tt = am.GetType(TypeName);

            amOut = am;
            return tt;
        }
        public static Type GetType(string TypeName)
        {
            Type t = null;
            Assembly am = null;
            t = GetType(TypeName, out am);

            return t;
        }

        /// <summary>
        /// 对比两个实体类属性值的差异
        /// </summary>
        /// <typeparam name="T">实体类</typeparam>
        /// <param name="oldMod">原实体类</param>
        /// <param name="newMod">新实体类</param>
        /// <returns>差异记录</returns>
        public static string GetChangeData<T>(T oldMod, T newMod)
        {
            if (oldMod == null || newMod == null)
                return "";

            string updateData = "";
            Type typeDescription = typeof(DescriptionAttribute);
            System.Reflection.PropertyInfo[] mPi = typeof(T).GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);

            for (int i = 0; i < mPi.Length; i++)
            {
                System.Reflection.PropertyInfo pi = mPi[i];
                object[] arr = pi.GetCustomAttributes(typeDescription, true);
                string atrr = arr.Length > 0 ? ((DescriptionAttribute)arr[0]).Description : pi.Name;

                object oldObj = pi.GetValue(oldMod, null);
                object newObj = pi.GetValue(newMod, null);
                string oldValue = oldObj == null ? "" : oldObj.ToString();
                string newValue = newObj == null ? "" : newObj.ToString();
                if (oldValue != newValue)
                {
                    updateData += atrr + ":Changed from " + oldValue + " to " + newValue + "\r\n";
                }
            }

            if (string.IsNullOrEmpty(updateData))
                return null;
            else
                return updateData;
        }

        /// <summary>
        /// 更新实体模型
        /// </summary>
        /// <typeparam name="T">源实体类型</typeparam>
        /// <typeparam name="S">最终合并后返回的实体类型</typeparam>
        /// <param name="tModel">源数据实体</param>
        /// <param name="outModel">最终合并后返回的实体</param>
        /// <returns>最终实体</returns>
        public static S EntityMerge<T, S>(T tModel, S outModel)
            where T : class
            where S : class
        {
            Type type = tModel.GetType();
            Type outType = outModel.GetType();
            var properties = type.GetProperties();
            var outProperties = outType.GetProperties();
            foreach (var property in properties)
            {
                foreach (var item in outProperties)
                {
                    if (property.Name == item.Name)
                    {
                        var value = property.GetValue(tModel);
                        item.SetValue(outModel, value, null);
                        break;
                    }
                }
            }
            return outModel;
        }

        #region---反射的私有成员---不常用的操作

        /// <summary>
        /// 得到私有字段的值
        /// </summary>
        public static T GetPrivateField<T>(object instance, string fieldname)
        {
            if (instance == null)
                return default(T);

            BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
            Type type = instance.GetType();
            FieldInfo field = type.GetField(fieldname, flag);
            return (T)field.GetValue(instance);
        }
        /// <summary>
        /// 得到私有属性的值
        /// </summary>
        public static T GetPrivateProperty<T>(object instance, string propertyname)
        {
            if (instance == null)
                return default(T);

            BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
            Type type = instance.GetType();
            PropertyInfo field = type.GetProperty(propertyname, flag);
            return (T)field.GetValue(instance, null);
        }
        /// <summary>
        /// 设置私有属性的值
        /// </summary>
        public static void SetPrivateProperty(object instance, string propertyname, object value)
        {
            if (instance == null)
                return;

            BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
            Type type = instance.GetType();
            PropertyInfo field = type.GetProperty(propertyname, flag);
            field.SetValue(instance, value, null);
        }
        /// <summary>
        /// 调用私有方法
        /// </summary>
        public static T CallPrivateMethod<T>(object instance, string MethodName, params object[] param)
        {
            if (instance == null)
                return default(T);

            BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
            Type type = instance.GetType();
            MethodInfo method = type.GetMethod(MethodName, flag);
            return (T)method.Invoke(instance, param);
        }
        /// <summary>
        /// 反射执行类实例的公开方法
        /// </summary>
        public static T CallMethod<T>(object instance, string MethodName, object[] parameters) where T : class, new()
        {
            if (instance == null)
                return default(T);

            Type tt = instance.GetType();
            MethodInfo m = tt.GetMethod(MethodName);
            if (m != null)
                return (T)m.Invoke(instance, parameters);
            else
                return null;
        }

        #endregion

        #region---反射的属性

        /// <summary>  
        /// 根据属性获取对应的属性名称  
        /// </summary>  
        /// <typeparam name="T">对象类型</typeparam>  
        /// <typeparam name="K">对象数据的类型</typeparam>  
        /// <param name="t">对象</param>  
        /// <param name="expr">需要获取的属性</param>  
        /// <returns>属性名</returns>  
        public static string GetPropertyName<T, K>(Expression<Func<T, K>> expr) where T : class
        {
            string propertyName = string.Empty;    //返回的属性名  
            //对象是不是一元运算符  
            if (expr.Body is UnaryExpression)
            {
                propertyName = ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member.Name;
            }
            //对象是不是访问的字段或属性  
            else if (expr.Body is MemberExpression)
            {
                propertyName = ((MemberExpression)expr.Body).Member.Name;
            }
            //对象是不是参数表达式  
            else if (expr.Body is ParameterExpression)
            {
                propertyName = ((ParameterExpression)expr.Body).Type.Name;
            }
            return propertyName;

            //eee为一个类型---例子
            //var name = GetPropertyName<eee>(p => p.ExtendedArray);  
        }

        public static string GetPropertyName<T>(Expression<Func<T, object>> expr) where T : class
        {
            string propertyName = string.Empty;
            if (expr.Body is UnaryExpression)
            {
                propertyName = ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member.Name;
            }
            else if (expr.Body is MemberExpression)
            {
                propertyName = ((MemberExpression)expr.Body).Member.Name;
            }
            else if (expr.Body is ParameterExpression)
            {
                propertyName = ((ParameterExpression)expr.Body).Type.Name;
            }
            return propertyName;
        }

        /// <summary>
        /// 反射取类实例对应属性的值--泛型
        /// </summary>
        public static object GetPropertyValue<T>(T enValue, string propertyName) where T : class
        {
            if (enValue == null)
                return null;

            PropertyInfo[] ps = enValue.GetType().GetProperties();
            foreach (PropertyInfo p in ps)
            {
                if (p.Name.Equals(propertyName))
                {
                    return p.GetValue(enValue, null);
                }
            }
            return null;
        }

        /// <summary>
        /// 反射取类实例对应属性的类型--泛型
        /// </summary>
        public static Type GetPropertyType<T>(string propertyName) where T : class
        {
            if (string.IsNullOrEmpty(propertyName))
                return null;

            PropertyInfo ps = typeof(T).GetProperty(propertyName);
            if (ps == null)
                return null;
            else
            {
                Type pt = ps.PropertyType;
                if (pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>))
                    pt = pt.GetGenericArguments()[0];

                return pt;
            }
        }

        /// <summary>
        /// 反射取类实例对应属性的值
        /// </summary>
        public static object GetPropertyValue(object enValue, string propertyName)
        {
            if (enValue == null)
                return null;

            PropertyInfo[] ps = enValue.GetType().GetProperties();
            foreach (PropertyInfo p in ps)
            {
                if (p.Name == propertyName)
                {
                    return p.GetValue(enValue, null);
                }
            }
            return null;
        }

        /// <summary>
        /// 反射设置类实例对应属性的值
        /// </summary>
        public static void SetValue(object en, object EnValue, string propertyName)
        {
            if (en == null)
                return;

            PropertyInfo[] ps = en.GetType().GetProperties();
            foreach (PropertyInfo p in ps)
            {
                if (p.Name == propertyName)
                {
                    Type pt = p.PropertyType;
                    object val = null;
                    if (pt == typeof(DateTime))
                    {
                        DateTime date = DateTime.Now;
                        if (DateTime.TryParse(EnValue.ToString(), out date))
                            val = date;
                    }
                    else if (pt == typeof(Guid))
                    {
                        Guid G = Guid.Empty;
                        if (Guid.TryParse(EnValue.ToString(), out G))
                            val = G;
                    }
                    else if (pt == typeof(int))
                    {
                        int G = 0;
                        if (int.TryParse(EnValue.ToString(), out G))
                            val = G;
                    }
                    else if (pt == typeof(long))
                    {
                        long G = 0;
                        if (long.TryParse(EnValue.ToString(), out G))
                            val = G;
                    }
                    else if (pt == typeof(decimal))
                    {
                        decimal G = 0;
                        if (decimal.TryParse(EnValue.ToString(), out G))
                            val = G;
                    }
                    else if (pt == typeof(double))
                    {
                        double G = 0;
                        if (double.TryParse(EnValue.ToString(), out G))
                            val = G;
                    }
                    else if (pt == typeof(bool))
                    {
                        bool G = false;
                        if (bool.TryParse(EnValue.ToString(), out G))
                            val = G;
                    }
                    else
                    {
                        val = EnValue;
                    }

                    p.SetValue(en, val, null);
                    return;
                }
            }
        }

        /// <summary>
        /// 实例化指定类型的实例（HOF）
        /// </summary>
        public static T InstantiateType<T>(Type type)
        {
            if (type == null)
                throw new ArgumentNullException(nameof(type), "Cannot instantiate null");

            ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
            if (ci == null)
                throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);

            return (T)ci.Invoke(new object[0]);
        }

        /// <summary>
        /// Sets the object properties using reflection.
        /// </summary>
        public static void SetObjectProperties(object obj, string[] propertyNames, object[] propertyValues)
        {
            for (int i = 0; i < propertyNames.Length; i++)
            {
                string name = propertyNames[i];
                string propertyName = CultureInfo.InvariantCulture.TextInfo.ToUpper(name.Substring(0, 1)) +
                                      name.Substring(1);

                SetPropertyValue(obj, propertyName, propertyValues[i]);
            }
        }

        public static void SetPropertyValue(object target, string propertyName, object value)
        {
            if (target == null)
                return;

            Type t = target.GetType();
            PropertyInfo pi = t.GetProperty(propertyName);
            if (pi == null || !pi.CanWrite)
            {
                foreach (var interfaceType in target.GetType().GetInterfaces())
                {
                    pi = interfaceType.GetProperty(propertyName);
                    if (pi != null && pi.CanWrite)
                        break;
                }
            }

            if (pi == null)
                throw new MemberAccessException($"No writable property '{propertyName}' found");

            MethodInfo mi = pi.GetSetMethod();
            if (mi == null)
                throw new MemberAccessException($"Property '{propertyName}' has no setter");

            value = ConvertValueIfNecessary(mi.GetParameters()[0].ParameterType, value);
            mi.Invoke(target, new[] { value });
        }

        public static object ConvertValueIfNecessary(Type requiredType, object newValue)
        {
            if (newValue != null)
            {
                if (requiredType.IsInstanceOfType(newValue))
                    return newValue;

                TypeConverter typeConverter = TypeDescriptor.GetConverter(requiredType);
                if (typeConverter.CanConvertFrom(newValue.GetType()))
                    return typeConverter.ConvertFrom(null, CultureInfo.InvariantCulture, newValue);

                typeConverter = TypeDescriptor.GetConverter(newValue.GetType());
                if (typeConverter.CanConvertTo(requiredType))
                    return typeConverter.ConvertTo(null, CultureInfo.InvariantCulture, newValue, requiredType);

                if (requiredType == typeof(Type))
                    return Type.GetType(newValue.ToString(), true);

                if (newValue.GetType().GetTypeInfo().IsEnum)
                    return ConvertValueIfNecessary(requiredType, Convert.ChangeType(newValue, Convert.GetTypeCode(newValue), null));

                throw new NotSupportedException(newValue + " is no a supported value for a target of type " + requiredType);
            }

            if (requiredType.GetTypeInfo().IsValueType)
                return Activator.CreateInstance(requiredType);

            // return default
            return null;
        }

        #endregion        

    }
}
